#pragma once

#include "Vectors.hpp"
#include "Quaternion.hpp"
#include "Entity.hpp"
#include "ListProxy.hpp"

#include <list>
#include <boost/noncopyable.hpp>

namespace cge {
	class SceneManager;
	class SceneNode;
	class Renderer;

	class SceneNode: boost::noncopyable {
	public:
		// collection types
		typedef std::list<SceneNode*> SceneNodes;
		typedef ListProxy<SceneNodes> NodesProxy;
		
		// dtor
		virtual ~SceneNode();

		// manager access
		SceneManager&       getManager()       { return mngr_; }
		const SceneManager& getManager() const { return mngr_; }

		// tree methods
		void              setParent   ( SceneNode& newParent );
		void              explode     ();
		NodesProxy&       getChildren ();
		SceneNode&        getParent   () const;
		bool              isRoot      () const;
		const NodesProxy& getChildren () const;

		
		// transformation methods
		void setPosition     ( const vec3& newPosition );
		void setRotation     ( const Quaternion& newRotation );

		void localTranslate  ( const vec3& with );
		void worldTranslate  ( const vec3& with );
		void parentTranslate ( const vec3& with );

		void localRotate     ( const Quaternion& by );
		void worldRotate     ( const Quaternion& by );
		void parentRotate    ( const Quaternion& by );

		const vec3&       getPosition() const;
		const Quaternion& getRotation() const;

		const vec3&       getWorldPosition();
		const Quaternion& getWorldRotation();

		// entity methods
		void               attachEntity( Entity& entity );
		void               detachEntity();

		Entity&            getEntity()       { return *entity_; }
		bool               hasEntity() const { return entity_!=0; }
		
		// rendering methods
		void render        ();
		void renderAll     ();
		void setVisibility ( bool newVisibility, bool propagate=true );
		void setRenderable ( bool newRenderable );
		bool isVisible     () const { return visible_ && renderable_; }
		bool isRenderable  () const { return renderable_; }

		void update() {
			if(needUpdate_) { 
				needUpdate_ = false;
				updateTransform_();
				if( entity_ )
					entity_->onTransformed();
				performUpdate();
			}
		}

		// overriden by manager-specific node
		virtual SceneNode* createChild() = 0;

	protected:
		// c-tors
		SceneNode( SceneManager&     manager );
		SceneNode( SceneNode&        parent );
		
		// overriden by manager-specific node
		virtual void performUpdate() = 0;

	private:
		typedef SceneNodes::iterator  SceneNodeIter;

		void requireUpdate_();
		void addChild_( SceneNode *child );
		void removeChild_( SceneNode *child );
		void updateTransform_();
		void applyTransform_();
		
		SceneManager& mngr_;

		Quaternion localRot_, worldRot_;
		vec3       localPos_, worldPos_;
	
		SceneNode* parent_;
		Entity*    entity_;
		SceneNodes children_;
		NodesProxy childrenProxy_;
		
		bool   needUpdate_;
		bool   dirtyTransform_;
		bool   visible_;
		bool   renderable_;
		double transform_[16];
	};
}